﻿using System;
using System.Runtime.CompilerServices;
using System.Text;

namespace IOP.Extension.Convert
{
    /// <summary>
    /// 十六进制转换扩展
    /// </summary>
    public static class HexConvertExtension
    {
        /// <summary>
        /// 将十六进制字符串转换为字节数组
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte[] HexStringToBytes(this string source)
        {
            ReadOnlySpan<char> chars = source.AsSpan();
            byte[] result = new byte[chars.Length / 2];
            for (int i = 0; i < chars.Length;)
            {
                result[i / 2] = Merge(ref chars, ref i);
            }
            return result;
        }

        /// <summary>
        /// 将十六进制字符串转换为字节数组
        /// </summary>
        /// <param name="source"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte[] HexStringToBytes(this string source, char separator)
        {
            string[] byteChars = source.Split(separator);
            byte[] result = new byte[byteChars.Length];
            for(int i = 0; i < byteChars.Length; i++)
            {
                string local = byteChars[i];
                if (local.Length > 2) throw new IndexOutOfRangeException($"hex string value {local} is out of length ,max length value is 2, please confirm separator is right");
                if (local.Length == 1) result[i] = GetHexByte(local[0]);
                else if (local.Length == 2) result[i] = (byte)((GetHexByte(local[0]) << 4) | GetHexByte(local[1]));
                else if (local.Length == 0) result[i] = 0;
            }
            return result;
        }

        /// <summary>
        /// 将字节数组转换成十六进制字符串
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="index">起始下标</param>
        /// <param name="length">长度</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static string BytesToHexString(this ref ReadOnlySpan<byte> data, ref int index, int length)
        {
            if (index + length > data.Length) throw new IndexOutOfRangeException("the end position is big than data's length");
            StringBuilder builder = new StringBuilder();
            for(int i = index; i < index + length; i++)
            {
                builder.Append(data[i].ToString("X2"));
            }
            index += length;
            return builder.ToString();
        }

        /// <summary>
        /// 将字节数组转换成十六进制字符串并在每个字节期间添加分隔符
        /// </summary>
        /// <param name="data">数据</param>
        /// <param name="index">起始下标</param>
        /// <param name="length">长度</param>
        /// <param name="separator">分隔符</param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static string BytesToHexString(this ref ReadOnlySpan<byte> data, ref int index, int length, char separator)
        {
            if (index + length > data.Length) throw new IndexOutOfRangeException("the end position is big than data's length");
            StringBuilder builder = new StringBuilder();
            for (int i = index; i < index + length; i++)
            {
                builder.Append(data[i].ToString("X2"));
                builder.Append(separator);
            }
            index += length;
            builder.Remove(builder.Length - 1, 1);
            return builder.ToString();
        }


        /// <summary>
        /// 合并
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte Merge(ref ReadOnlySpan<char> source, ref int index)
        {
            return (byte)((GetHexByte(ref source, ref index) << 4) | GetHexByte(ref source, ref index));
        }
        /// <summary>
        /// 从十六进制字符串获取单个字节
        /// </summary>
        /// <param name="source"></param>
        /// <param name="index"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte GetHexByte(ref ReadOnlySpan<char> source, ref int index)
        {
            return GetHexByte(source[index++]);
        }
        /// <summary>
        /// 将当个字符转换为byte
        /// </summary>
        /// <param name="code"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static byte GetHexByte(char code)
        {
            {
                switch (code)
                {
                    case '0':
                        return 0x0;
                    case '1':
                        return 0x1;
                    case '2':
                        return 0x2;
                    case '3':
                        return 0x3;
                    case '4':
                        return 0x4;
                    case '5':
                        return 0x5;
                    case '6':
                        return 0x6;
                    case '7':
                        return 0x7;
                    case '8':
                        return 0x8;
                    case '9':
                        return 0x9;
                    case 'a':
                    case 'A':
                        return 0xa;
                    case 'b':
                    case 'B':
                        return 0xb;
                    case 'c':
                    case 'C':
                        return 0xc;
                    case 'd':
                    case 'D':
                        return 0xd;
                    case 'e':
                    case 'E':
                        return 0xe;
                    case 'f':
                    case 'F':
                        return 0xf;
                    default:
                        throw new Exception($"Cannot convert hexString to bytes, {code} is not a hexchar");
                }
            }
        }
    }
}
